/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */

package org.huihoo.orbas.orb.giop;

/**
 * <p>Description: </p>
 * Server side GIOP implementation.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */

import org.huihoo.orbas.orb.ORB;
import org.huihoo.orbas.orb.cdr.CDRInputStream;
import org.huihoo.orbas.orb.cdr.CDROutputStream;
import org.huihoo.orbas.orb.protocols.Transport;
import org.omg.IOP.ServiceContext;
//import org.omg.GIOP.*;
//import org.omg.IOP.*;

public class GiopServerWorker extends GiopWorker implements
		org.huihoo.orbas.orb.ServerWorker,
		org.omg.CORBA.portable.ResponseHandler {

	protected Transport transport;

	private java.io.InputStream in;
	private java.io.OutputStream out;
	private ORB orb;

	int requestId;

	int version;

	public GiopServerWorker(ORB orb, int version) {
		this.orb = orb;
		this.version = version;
	}

	public GiopServerWorker(ORB orb, Transport transport, int version) {
		this.orb = orb;
		this.transport = transport;
		if (transport == null) {
			System.out.println("GiopServerWorker:got null transport");
		}
		this.in = transport.getInputStream();
		this.out = transport.getOutputStream();
		this.version = version;
	}

	public void run() {

		while (true) {
			try {

				int messageHeaderLength = 12;

				byte[] buffer = new byte[messageHeaderLength];
				int bytesRead = 0;

				try {
					if ((bytesRead = in.read(buffer, 0, messageHeaderLength)) == -1) {
						System.out.println("Connection closed by peer");
						return;
					}
				} catch (java.io.IOException e) {
					// System.out.println("Communication Error:" +
					// e.toString());
					// e.printStackTrace();
					return;
				}

				if (bytesRead != messageHeaderLength) {
					System.out
							.println("Protocol violation: Wrong GIOP Header length of "
									+ bytesRead);
					return;
				}

				if (buffer[0] != 0x47 || buffer[1] != 0x49 || buffer[2] != 0x4f
						|| buffer[3] != 0x50) {
					System.out
							.println("Protocol violation: Malformed GIOP Header");

					// send back GIOP Error diagram
					return;
				}

				int messageSize;
				boolean isLittleEndian;

				CDRInputStream istream = new CDRInputStream(orb, buffer);

				istream.read_long();
				org.omg.GIOP.Version version = org.omg.GIOP.VersionHelper
						.read(istream);

				// pre-declaration of local variavles
				byte reponseExpected;
				CDRInputStream headerInput;
				byte[] mesg;
				org.omg.GIOP.TargetAddress target;
				CDRInputStream msgIn = null;
				int type;

				switch (version.major) {
				case 1:
					switch (version.minor) {
					case 0:

						headerInput = new CDRInputStream(orb, buffer);

						headerInput.mark();
						org.omg.GIOP.MessageHeader_1_0 header0 = org.omg.GIOP.MessageHeader_1_0Helper
								.read(headerInput);

						isLittleEndian = header0.byte_order;

						headerInput.setLittleEndian(isLittleEndian);

						headerInput.reset();
						header0 = org.omg.GIOP.MessageHeader_1_0Helper
								.read(headerInput);
						messageSize = header0.message_size;

						mesg = new byte[messageSize];

						try {
							if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
								System.out.println("Connection closed by peer");
								return;
							}
						} catch (java.io.IOException e) {
							System.out.println("Communication Error:"
									+ e.toString());
							return;
						}

						if (bytesRead != messageSize) {
							System.out
									.println("GIOP message not of correct length");
							return;
						}

						msgIn = new CDRInputStream(orb, mesg);
						msgIn.setLittleEndian(isLittleEndian);

						this.version = 10;

						type = (int) header0.message_type;
						switch (type) {
						case org.omg.GIOP.MsgType_1_0._Request:
							org.omg.GIOP.RequestHeader_1_0 request = org.omg.GIOP.RequestHeader_1_0Helper
									.read(msgIn);

							target = new org.omg.GIOP.TargetAddress();
							target.object_key(request.object_key);

							if (request.response_expected == true) {
								reponseExpected = 1;
							} else {
								reponseExpected = 0;
							}

							handleRequest(request.request_id,
									request.operation, request.service_context,
									target, reponseExpected, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_0._Reply:
							org.omg.GIOP.ReplyHeader_1_0 reply = org.omg.GIOP.ReplyHeader_1_0Helper
									.read(msgIn);

							handleReply(reply.request_id,
									reply.reply_status.value(),
									reply.service_context, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_0._LocateRequest:
							org.omg.GIOP.LocateRequestHeader_1_0 locateRequest = org.omg.GIOP.LocateRequestHeader_1_0Helper
									.read(msgIn);

							target = new org.omg.GIOP.TargetAddress();
							target.object_key(locateRequest.object_key);
							handleLocateRequest(locateRequest.request_id,
									target, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_0._LocateReply:
							org.omg.GIOP.LocateReplyHeader_1_0 locateReply = org.omg.GIOP.LocateReplyHeader_1_0Helper
									.read(msgIn);

							handleLocateReply(locateReply.request_id,
									locateReply.locate_status.value(), msgIn);
							break;

						case org.omg.GIOP.MsgType_1_0._CancelRequest:
							org.omg.GIOP.CancelRequestHeader cancelRequest = org.omg.GIOP.CancelRequestHeaderHelper
									.read(msgIn);

							handleCancelRequest(cancelRequest.request_id);

						case org.omg.GIOP.MsgType_1_0._CloseConnection:

							handleCloseConnection();
							break;

						case org.omg.GIOP.MsgType_1_0._MessageError:

							handleMessageError();
							break;

						default:
							// error
						}

						break; // minor version is 0

					case 1:

						headerInput = new CDRInputStream(orb, buffer);

						headerInput.mark();
						org.omg.GIOP.MessageHeader_1_1 header1 = org.omg.GIOP.MessageHeader_1_1Helper
								.read(headerInput);

						if ((header1.flags & 0x01) == 0x1) {
							isLittleEndian = true;
						} else {
							isLittleEndian = false;
						}

						headerInput.setLittleEndian(isLittleEndian);

						headerInput.reset();
						header1 = org.omg.GIOP.MessageHeader_1_1Helper
								.read(headerInput);
						messageSize = header1.message_size;

						mesg = new byte[messageSize];

						try {
							if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
								System.out.println("Connection closed by peer");
								return;
							}
						} catch (java.io.IOException e) {
							System.out.println("Communication Error:"
									+ e.toString());
							e.printStackTrace();
							return;
						}

						if (bytesRead != messageSize) {
							System.out
									.println("GIOP msg not of correct length");
							return;
						}

						msgIn = new CDRInputStream(orb, mesg);
						msgIn.setLittleEndian(isLittleEndian);

						this.version = 11;

						type = (int) header1.message_type;
						switch (type) {
						case org.omg.GIOP.MsgType_1_1._Request:
							org.omg.GIOP.RequestHeader_1_1 request = org.omg.GIOP.RequestHeader_1_1Helper
									.read(msgIn);

							target = new org.omg.GIOP.TargetAddress();
							target.object_key(request.object_key);

							if (request.response_expected == true) {
								reponseExpected = 1;
							} else {
								reponseExpected = 0;
							}

							handleRequest(request.request_id,
									request.operation, request.service_context,
									target, reponseExpected, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._Reply:
							org.omg.GIOP.ReplyHeader_1_0 reply = org.omg.GIOP.ReplyHeader_1_1Helper
									.read(msgIn);

							handleReply(reply.request_id,
									reply.reply_status.value(),
									reply.service_context, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._LocateRequest:
							org.omg.GIOP.LocateRequestHeader_1_0 locateRequest = org.omg.GIOP.LocateRequestHeader_1_1Helper
									.read(msgIn);

							target = new org.omg.GIOP.TargetAddress();
							target.object_key(locateRequest.object_key);
							handleLocateRequest(locateRequest.request_id,
									target, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._LocateReply:
							org.omg.GIOP.LocateReplyHeader_1_0 locateReply = org.omg.GIOP.LocateReplyHeader_1_1Helper
									.read(msgIn);

							handleLocateReply(locateReply.request_id,
									locateReply.locate_status.value(), msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._CancelRequest:
							org.omg.GIOP.CancelRequestHeader cancelRequest = org.omg.GIOP.CancelRequestHeaderHelper
									.read(msgIn);

							handleCancelRequest(cancelRequest.request_id);

						case org.omg.GIOP.MsgType_1_1._CloseConnection:
							handleCloseConnection();
							break;

						case org.omg.GIOP.MsgType_1_1._MessageError:
							handleMessageError();
							break;

						case org.omg.GIOP.MsgType_1_1._Fragment:
							handleFragment();
							break;

						default:
							// error
						}

						break; // minor version is 1

					case 2:

						headerInput = new CDRInputStream(orb, buffer);

						headerInput.mark();
						org.omg.GIOP.MessageHeader_1_1 header2 = org.omg.GIOP.MessageHeader_1_2Helper
								.read(headerInput);

						if ((header2.flags & 0x01) == 0x1) {
							isLittleEndian = true;
						} else {
							isLittleEndian = false;
						}

						headerInput.setLittleEndian(isLittleEndian);

						headerInput.reset();
						header2 = org.omg.GIOP.MessageHeader_1_2Helper
								.read(headerInput);
						messageSize = header2.message_size;

						mesg = new byte[messageSize];

						try {
							if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
								System.out.println("Connection closed by peer");
								return;
							}
						} catch (java.io.IOException e) {
							System.out.println("Communication Error:"
									+ e.toString());
							e.printStackTrace();
							return;
						}

						if (bytesRead != messageSize) {
							System.out
									.println("GIOP msg not of correct length");
							return;
						}

						msgIn = new CDRInputStream(orb, mesg);
						msgIn.setLittleEndian(isLittleEndian);

						this.version = 12;

						type = (int) header2.message_type;
						switch (type) {
						case org.omg.GIOP.MsgType_1_1._Request:
							org.omg.GIOP.RequestHeader_1_2 request = org.omg.GIOP.RequestHeader_1_2Helper
									.read(msgIn);

							handleRequest(request.request_id,
									request.operation, request.service_context,
									request.target, request.response_flags,
									msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._Reply:
							org.omg.GIOP.ReplyHeader_1_2 reply = org.omg.GIOP.ReplyHeader_1_2Helper
									.read(msgIn);

							handleReply(reply.request_id,
									reply.reply_status.value(),
									reply.service_context, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._LocateRequest:
							org.omg.GIOP.LocateRequestHeader_1_2 locateRequest = org.omg.GIOP.LocateRequestHeader_1_2Helper
									.read(msgIn);

							handleLocateRequest(locateRequest.request_id,
									locateRequest.target, msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._LocateReply:
							org.omg.GIOP.LocateReplyHeader_1_2 locateReply = org.omg.GIOP.LocateReplyHeader_1_2Helper
									.read(msgIn);

							handleLocateReply(locateReply.request_id,
									locateReply.locate_status.value(), msgIn);
							break;

						case org.omg.GIOP.MsgType_1_1._CancelRequest:
							org.omg.GIOP.CancelRequestHeader cancelRequest = org.omg.GIOP.CancelRequestHeaderHelper
									.read(msgIn);

							handleCancelRequest(cancelRequest.request_id);

						case org.omg.GIOP.MsgType_1_1._CloseConnection:
							handleCloseConnection();
							break;

						case org.omg.GIOP.MsgType_1_1._MessageError:
							handleMessageError();
							break;

						case org.omg.GIOP.MsgType_1_1._Fragment:
							handleFragment();
							break;

						default:
							// error
						}

						break; // minor version is 2

					default:
					}

					break; // major version is 1

				default:
					// error
				}
			} catch (Exception ex) {
				System.out.println("Exception: " + ex.toString());

				// ignore this,go to next message
				continue;
			}
		}

	}

	public void handleRequest(int request_id, String op,
			ServiceContext[] context, org.omg.GIOP.TargetAddress target,
			byte response_flags, CDRInputStream in) {

		this.requestId = request_id;
		CDROutputStream bodyOut = null;
		try {

			switch (target.discriminator()) {
			case org.omg.GIOP.KeyAddr.value:
				bodyOut = (CDROutputStream) (orb.getDefaultIopEngine().invoke(
						target.object_key(), op, in, this));

			case org.omg.GIOP.ProfileAddr.value:
				// unpack profiles to objectkey
				break;

			case org.omg.GIOP.ReferenceAddr.value:
				// unpack ior to objectkey
				break;

			default:
				// error

			}

		} catch (org.omg.PortableServer.ForwardRequest forward) {
			// user request location forward
			// create a forward reply
			bodyOut = (CDROutputStream) createForwardReply(forward);
		} catch (org.omg.CORBA.SystemException se) {
			bodyOut = (CDROutputStream) createExceptionReply();
		} catch (org.omg.PortableServer.POAPackage.AdapterNonExistent ue) {
			bodyOut = (CDROutputStream) createExceptionReply();
			org.omg.PortableServer.POAPackage.AdapterNonExistentHelper.write(
					bodyOut, ue);
		}

		// create GIOP header
		CDROutputStream headerOut = new CDROutputStream(null);

		org.omg.GIOP.MessageHeader_1_0 messageHeader10 = new org.omg.GIOP.MessageHeader_1_0();

		messageHeader10.magic = new char[4];
		messageHeader10.magic[0] = 'G';
		messageHeader10.magic[1] = 'I';
		messageHeader10.magic[2] = 'O';
		messageHeader10.magic[3] = 'P';
		messageHeader10.GIOP_version = new org.omg.GIOP.Version();
		messageHeader10.GIOP_version.major = 1;
		messageHeader10.GIOP_version.minor = (byte) (this.version - 10);
		messageHeader10.byte_order = false;
		messageHeader10.message_type = org.omg.GIOP.MsgType_1_0._Reply;
		messageHeader10.message_size = bodyOut.getLength() + 1;

		org.omg.GIOP.MessageHeader_1_0Helper.write(headerOut, messageHeader10);

		headerOut
				.write_octet_array(bodyOut.getBuffer(), 0, bodyOut.getLength());
		// write out GIOP header

		try {
			// write reply
			write(headerOut);

		} catch (java.io.IOException e) {
			e.printStackTrace();
		}

		// just for test
		// System.out.println("operation: " + op);
		// System.out.println("parameter: " + in.read_string());
		// end of test

	}

	public org.omg.CORBA.portable.OutputStream createReply() {

		CDROutputStream out = new CDROutputStream(orb);
		// create giop reply header(giop body)
		switch (version) {
		case 10:
		case 11:
			// version 1.0 and 1.1 are the same in fact
			org.omg.GIOP.ReplyHeader_1_0 reply10 = new org.omg.GIOP.ReplyHeader_1_0();
			reply10.service_context = new org.omg.IOP.ServiceContext[0];
			reply10.request_id = requestId;
			reply10.reply_status = org.omg.GIOP.ReplyStatusType_1_0.NO_EXCEPTION;

			org.omg.GIOP.ReplyHeader_1_0Helper.write(out, reply10);
			break;
		case 12:
			org.omg.GIOP.ReplyHeader_1_2 reply12 = new org.omg.GIOP.ReplyHeader_1_2();
			reply12.service_context = new org.omg.IOP.ServiceContext[0];
			reply12.request_id = requestId;
			reply12.reply_status = org.omg.GIOP.ReplyStatusType_1_2.NO_EXCEPTION;

			org.omg.GIOP.ReplyHeader_1_2Helper.write(out, reply12);
			break;
		default:
			// error
		}

		return out;
	}

	public org.omg.CORBA.portable.OutputStream createExceptionReply() {
		CDROutputStream out = new CDROutputStream(orb);
		// create giop reply header(giop body)
		switch (version) {
		case 10:
		case 11:
			org.omg.GIOP.ReplyHeader_1_0 reply10 = new org.omg.GIOP.ReplyHeader_1_0();
			reply10.service_context = new org.omg.IOP.ServiceContext[0];
			reply10.request_id = requestId;
			reply10.reply_status = org.omg.GIOP.ReplyStatusType_1_0.USER_EXCEPTION;

			org.omg.GIOP.ReplyHeader_1_0Helper.write(out, reply10);
			break;
		case 12:
			org.omg.GIOP.ReplyHeader_1_2 reply12 = new org.omg.GIOP.ReplyHeader_1_2();
			reply12.service_context = new org.omg.IOP.ServiceContext[0];
			reply12.request_id = requestId;
			reply12.reply_status = org.omg.GIOP.ReplyStatusType_1_2.USER_EXCEPTION;

			org.omg.GIOP.ReplyHeader_1_2Helper.write(out, reply12);
			break;
		default:
			// error
		}

		return out;
	}

	public org.omg.CORBA.portable.OutputStream createSystemExceptionReply() {
		CDROutputStream out = new CDROutputStream(orb);
		// create giop reply header(giop body)
		switch (version) {
		case 10:
		case 11:
			org.omg.GIOP.ReplyHeader_1_0 reply10 = new org.omg.GIOP.ReplyHeader_1_0();
			reply10.service_context = new org.omg.IOP.ServiceContext[0];
			reply10.request_id = requestId;
			reply10.reply_status = org.omg.GIOP.ReplyStatusType_1_0.SYSTEM_EXCEPTION;

			org.omg.GIOP.ReplyHeader_1_0Helper.write(out, reply10);
			break;
		case 12:
			org.omg.GIOP.ReplyHeader_1_2 reply12 = new org.omg.GIOP.ReplyHeader_1_2();
			reply12.service_context = new org.omg.IOP.ServiceContext[0];
			reply12.request_id = requestId;
			reply12.reply_status = org.omg.GIOP.ReplyStatusType_1_2.SYSTEM_EXCEPTION;

			org.omg.GIOP.ReplyHeader_1_2Helper.write(out, reply12);
			break;
		default:
			// error
		}

		return out;
	}

	public org.omg.CORBA.portable.OutputStream createForwardReply(
			org.omg.PortableServer.ForwardRequest forward) {
		return null;
	}

	public void handleReply(int request_id, int reply_status,
			ServiceContext[] context, CDRInputStream in) {

	}

	public void handleLocateRequest(int request_id,
			org.omg.GIOP.TargetAddress target, CDRInputStream in) {
		try {
			orb.getDefaultIopEngine().locate();

		} catch (org.omg.PortableServer.ForwardRequest forward) {
			// user request location forward
			// create a forward reply
		}
	}

	public void handleLocateReply(int request_id, int locate_status,
			CDRInputStream in) {

	}

	public void handleCancelRequest(int request_id) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void handleCloseConnection() {
		throw new org.omg.CORBA.NO_IMPLEMENT();

	}

	public void handleMessageError() {
		System.out.println("GIOP Error Message received");

	}

	public void handleFragment() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public Transport getTransport() {
		return transport;
	}

}